﻿using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Collections;

namespace BOMS.Lib.Net
{


    public class TcpSvr
    {
        #region 定义字段

        /// <summary> 
        /// 默认的服务器最大连接客户端端数据 
        /// </summary> 
        public const int DefaultMaxClient = 100;

        /// <summary> 
        /// 接收数据缓冲区大小64K 
        /// </summary> 
        public const int DefaultBufferSize = 64 * 1024;

        /// <summary> 
        /// 最大数据报文大小 
        /// </summary> 
        public const int MaxDatagramSize = 640 * 1024;

        /// <summary> 
        /// 报文解析器 
        /// </summary> 
        private DatagramResolver _resolver;

        /// <summary> 
        /// 通讯格式编码解码器 
        /// </summary> 
        private Coder _coder;

        /// <summary> 
        /// 服务器程序允许的最大客户端连接数 
        /// </summary> 
        private ushort _maxClient;

        /// <summary> 
        /// 服务器的运行状态 
        /// </summary> 
        private bool _isRun;

        /// <summary> 
        /// 接收数据缓冲区 
        /// </summary> 
        private byte[] _recvDataBuffer;

        /// <summary> 
        /// 服务器使用的异步Socket类, 
        /// </summary> 
        private Socket _svrSock;

        /// <summary> 
        /// 保存所有客户端会话的哈希表 
        /// </summary> 
        private Hashtable _sessionTable;

        /// <summary> 
        /// 当前的连接的客户端数 
        /// </summary> 
        private ushort _clientCount;

        #endregion

        #region 事件定义

        /// <summary> 
        /// 客户端建立连接事件 
        /// </summary> 
        public event NetEvent ClientConn;

        /// <summary> 
        /// 客户端关闭事件 
        /// </summary> 
        public event NetEvent ClientClose;

        /// <summary> 
        /// 服务器已经满事件 
        /// </summary> 
        public event NetEvent ServerFull;

        /// <summary> 
        /// 服务器接收到数据事件 
        /// </summary> 
        public event NetEvent RecvData;

        #endregion

        #region 构造函数

        /// <summary> 
        /// 构造函数 
        /// </summary> 
        /// <param name="port">服务器端监听的端口号</param> 
        /// <param name="maxClient">服务器能容纳客户端的最大能力</param> 
        /// <param name="encodingMothord">通讯的编码方式</param> 
        public TcpSvr(ushort maxClient, Coder coder)
        {
            _maxClient = maxClient;
            _coder = coder;
        }


        /// <summary> 
        /// 构造函数(默认使用Default编码方式) 
        /// </summary> 
        /// <param name="port">服务器端监听的端口号</param> 
        /// <param name="maxClient">服务器能容纳客户端的最大能力</param> 
        public TcpSvr(ushort maxClient)
        {
            _maxClient = maxClient;//最多的客户端连接数
            _coder = new Coder(Coder.EncodingMothord.Default);//定义解码的规则
        }


        // <summary> 
        /// 构造函数(默认使用Default编码方式和DefaultMaxClient(100)个客户端的容量) 
        /// </summary> 
        /// <param name="port">服务器端监听的端口号</param> 
        public TcpSvr()
            : this(DefaultMaxClient)
        {
        }

        #endregion

        #region 属性

        /// <summary> 
        /// 服务器的Socket对象 
        /// </summary> 
        public Socket ServerSocket
        {
            get
            {
                return _svrSock;
            }
        }

        /// <summary> 
        /// 数据报文分析器 
        /// </summary> 
        public DatagramResolver Resovlver
        {
            get
            {
                return _resolver;
            }
            set
            {
                _resolver = value;
            }
        }

        /// <summary> 
        /// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改 
        /// </summary> 
        public Hashtable SessionTable
        {
            get
            {
                return _sessionTable;
            }
        }

        /// <summary> 
        /// 服务器可以容纳客户端的最大能力 
        /// </summary> 
        public int Capacity
        {
            get
            {
                return _maxClient;
            }
        }

        /// <summary> 
        /// 当前的客户端连接数 
        /// </summary> 
        public int SessionCount
        {
            get
            {
                return _clientCount;
            }
        }

        /// <summary> 
        /// 服务器运行状态 
        /// </summary> 
        public bool IsRun
        {
            get
            {
                return _isRun;
            }

        }

        #endregion

        #region 公有方法
        ///-----------------------------------------------------------------*
        /// <summary>
        /// 启动服务器程序,开始监听客户端请求
        /// </summary>
        /// <param name="port">服务器监听的端口 号</param>
        ///-----------------------------------------------------------------*
        public virtual void Start(ushort port)
        {
            if (_isRun)
            {
                throw (new ApplicationException("TcpSvr已经在运行."));
            }

            _sessionTable = new Hashtable(53);

            _recvDataBuffer = new byte[DefaultBufferSize];

            //初始化socket 
            _svrSock = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);

            //绑定端口 
            IPEndPoint iep = new IPEndPoint(IPAddress.Any, port);//IPAddress.Any
            _svrSock.Bind(iep);

            //开始监听 
            _svrSock.Listen(5);

            //设置异步方法接受客户端连接 
            _svrSock.BeginAccept(new AsyncCallback(AcceptConn), _svrSock);

            _isRun = true;

        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 停止服务器程序,所有与客户端的连接将关闭 
        /// </summary> 
        ///-----------------------------------------------------------------*
        public virtual void Stop()
        {
            if (!_isRun)
            {
                throw (new ApplicationException("TcpSvr已经停止"));
            }

            //这个条件语句，一定要在关闭所有客户端以前调用 
            //否则在EndConn会出现错误 
            _isRun = false;

            //关闭数据连接,否则客户端会认为是强制关闭连接 
            if (_svrSock.Connected)
            {
                _svrSock.Shutdown(SocketShutdown.Both);
            }

            CloseAllClient();
            //清理资源 
            _svrSock.Close();

            _sessionTable = null;

        }

        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 关闭所有的客户端会话,与所有的客户端连接会断开 
        /// </summary> 
        ///-----------------------------------------------------------------*
        public virtual void CloseAllClient()
        {
            foreach (Session client in _sessionTable.Values)
            {
                client.Close();
            }
            _clientCount = 0;//清除连接
            _sessionTable.Clear();
        }

        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 关闭一个与客户端之间的会话 
        /// </summary> 
        /// <param name="closeClient">需要关闭的客户端会话对象</param> 
        ///-----------------------------------------------------------------*
        public virtual void CloseSession(Session closeClient)
        {
            //Debug.Assert(closeClient != null);

            if (closeClient != null)
            {
                closeClient.Datagram = null;

                _sessionTable.Remove(closeClient.ID);

                _clientCount--;

                //客户端强制关闭链接 
                if (ClientClose != null)
                {
                    ClientClose(this, new NetEventArgs(closeClient));
                    
                }
                closeClient.Close();
                closeClient = null;
            }
        }

        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 发送数据 
        /// </summary> 
        /// <param name="recvDataClient">接收数据的客户端会话</param> 
        /// <param name="datagram">数据报文</param> 
        ///-----------------------------------------------------------------*
        public virtual void Send(Session recvDataClient, string datagram)
        {
            //获得数据编码 
            byte[] data = _coder.GetEncodingBytes(datagram);

            recvDataClient.ClientSocket.BeginSend
                (data, 0, data.Length, SocketFlags.None,
            new AsyncCallback(SendDataEnd), recvDataClient.ClientSocket);

        }

        #endregion

        #region 受保护方法
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 关闭一个客户端Socket,首先需要关闭Session 
        /// </summary> 
        /// <param name="client">目标Socket对象</param> 
        /// <param name="exitType">客户端退出的类型</param> 
        ///-----------------------------------------------------------------*
        protected virtual void CloseClient
            (Socket client, Session.ExitType exitType)
        {
            Debug.Assert(client != null);

            //查找该客户端是否存在,如果不存在,抛出异常 
            Session closeClient = FindSession(client);
            if (closeClient != null)
            {
                closeClient.TypeOfExit = exitType;
                CloseSession(closeClient);
            }
            else
            {
                //throw (new ApplicationException("需要关闭的Socket对象不存在"));
            }
        }

        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 客户端连接处理函数 
        /// </summary> 
        /// <param name="iar">欲建立服务器连接的Socket对象</param> 
        ///-----------------------------------------------------------------*
        protected virtual void AcceptConn(IAsyncResult iar)
        {
            //如果服务器停止了服务,就不能再接收新的客户端 
            if (!_isRun)
            {
                return;
            }

            //接受一个客户端的连接请求 
            Socket oldserver = (Socket)iar.AsyncState;

            Socket client = oldserver.EndAccept(iar);

            //检查是否达到最大的允许的客户端数目 
            if (_clientCount == _maxClient)
            {
                //服务器已满,发出通知 
                if (ServerFull != null)
                {
                    ServerFull(this, new NetEventArgs(new Session(client)));
                }

            }
            else
            {

                Session newSession = new Session(client);

                _sessionTable.Add(newSession.ID, newSession);

                //客户端引用计数+1 
                _clientCount++;

                //开始接受来自该客户端的数据 
                client.BeginReceive
                    (_recvDataBuffer, 0, _recvDataBuffer.Length, SocketFlags.None,
                new AsyncCallback(ReceiveData), client);

                //新的客户段连接,发出通知 
                if (ClientConn != null)
                {
                    ClientConn(this, new NetEventArgs(newSession));
                }
            }

            //继续接受客户端 
            _svrSock.BeginAccept(new AsyncCallback(AcceptConn), _svrSock);
        }

        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 通过Socket对象查找Session对象 
        /// </summary> 
        /// <param name="client"></param> 
        /// <returns>找到的Session对象,如果为null,说明并不存在该会话</returns> 
        ///-----------------------------------------------------------------*
        private Session FindSession(Socket client)
        {
            SessionId id = new SessionId((int)client.Handle);

            return (Session)_sessionTable[id];
        }
        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 接受数据完成处理函数，异步的特性就体现在这个函数中， 
        /// 收到数据后，会自动解析为字符串报文 
        /// </summary> 
        /// <param name="iar">目标客户端Socket</param> 
        ///-----------------------------------------------------------------*
        protected virtual void ReceiveData(IAsyncResult iar)
        {
            const byte nAAHeader = 170;
            const byte nBBHeader = 187;
            Socket client = (Socket)iar.AsyncState;
            try
            {
                //如果两次开始了异步的接收,所以当客户端退出的时候 
                //会两次执行EndReceive
                int recv = client.EndReceive(iar);
                if (recv == 0)
                {
                    //正常的关闭 
                    CloseClient(client, Session.ExitType.NormalExit);
                    return;
                }
               // string receivedData = _coder.GetEncodingString(_recvDataBuffer, recv);
                ByteArrayInfo bai = new ByteArrayInfo();
                bai.HexArray = new byte[recv];
                Array.Copy(_recvDataBuffer, bai.HexArray,(long)recv);
                bai.DataLength = recv;
                bai.PackPrefix = _recvDataBuffer[0];//AA:170 / BB:187

                //发布收到数据的事件 
                if (RecvData != null)
                {
                    Session sendDataSession = FindSession(client);

                    if (sendDataSession.ByteArrayList.Count == 0)
                    {
                        if (bai.PackPrefix == nAAHeader) //AA是头包，必须先入栈
                        {
                            sendDataSession.ByteArrayList.Add(bai);//存入Session
                        }
                        else
                        {
                            // do nothing.
                        }
                    }
                    else
                    {
                        ICloneable copySession = (ICloneable)sendDataSession;
                        Session clientSession = (Session)copySession.Clone();                       
                        if (bai.PackPrefix == nAAHeader) //AA是头包
                        {                           
                            RecvData(this, new NetEventArgs(clientSession));//转发旧AA包
                            sendDataSession.ByteArrayList.Clear();
                            sendDataSession.ByteArrayList.Add(bai);//暂存新包
                            //则，发送前面的AA包（认为没有拆包）
                        }
                        else if(bai.PackPrefix == nBBHeader) //BB是第二包
                        {
                            Byte[] contactArr = ConcatDataPack(sendDataSession.ByteArrayList[0].HexArray, bai.HexArray);
                            clientSession.ByteArrayList.Clear();
                            ByteArrayInfo concatOne = new ByteArrayInfo();
                            concatOne.HexArray = contactArr;
                            concatOne.PackPrefix = nAAHeader;
                            concatOne.DataLength = contactArr.Length;
                            clientSession.ByteArrayList.Add(concatOne);
                            RecvData(this, new NetEventArgs(clientSession));//转发合并后的AA+BB包
                            sendDataSession.ByteArrayList.Clear();//清空
                        }
                    }
                    /*
                    //如果定义了报文的尾标记,需要处理报文的多种情况 
                    if (_resolver != null)
                    {
                        if (sendDataSession.Datagram != null &&
                        sendDataSession.Datagram.Length != 0)
                        {
                            //加上最后一次通讯剩余的报文片断 
                            receivedData = sendDataSession.Datagram + receivedData;
                        }
                        string[] recvDatagrams = _resolver.Resolve(ref receivedData);
                        foreach (string newDatagram in recvDatagrams)
                        {
                            //深拷贝,为了保持Datagram的对立性 
                            ICloneable copySession = (ICloneable)sendDataSession;
                            Session clientSession = (Session)copySession.Clone();
                            clientSession.Datagram = newDatagram;
                            //发布一个报文消息 
                            RecvData(this, new NetEventArgs(clientSession));
                        }
                        //剩余的代码片断,下次接收的时候使用 
                        sendDataSession.Datagram = receivedData;

                        if (sendDataSession.Datagram.Length > MaxDatagramSize)
                        {
                            sendDataSession.Datagram = null;
                        }
                    }
                    //没有定义报文的尾标记,直接交给消息订阅者使用 
                    else
                    {
                     
                        ICloneable copySession = (ICloneable)sendDataSession;

                        Session clientSession = (Session)copySession.Clone();

                        clientSession.Datagram = receivedData;

                        RecvData(this, new NetEventArgs(clientSession));
                   }* */
                }//end of if(RecvData!=null)
                //继续接收来自来客户端的数据 
                client.BeginReceive
                    (_recvDataBuffer, 0, _recvDataBuffer.Length, SocketFlags.None,
                new AsyncCallback(ReceiveData), client);

            }
            catch (SocketException ex)
            {
                //客户端退出 
                if (10054 == ex.ErrorCode)
                {
                    //客户端强制关闭 
                    CloseClient(client, Session.ExitType.ExceptionExit);
                }

            }
            catch (ObjectDisposedException ex)
            {
                //当调用CloseSession()时,会结束数据接收,但是数据接收 
                //处理中会调用int recv = client.EndReceive(iar); 
                //就访问了CloseSession()已经处置的对象 
                if (ex != null)
                {
                    ex = null;
                    //DoNothing; 
                }
            }

        }

        ///-----------------------------------------------------------------*
        /// <summary> 
        /// 发送数据完成处理函数 
        /// </summary> 
        /// <param name="iar">目标客户端Socket</param> 
        ///-----------------------------------------------------------------*
        protected virtual void SendDataEnd(IAsyncResult iar)
        {
            Socket client = (Socket)iar.AsyncState;

            int sent = client.EndSend(iar);
        }
        private byte[] ConcatDataPack(byte[] arrA,byte[] arrB)
        {    
            /*
            if (arrA.Length < 8) return null;
            int dateLen = arrA.Length-5+arrB.Length-3;
            byte[] result = new byte[dateLen];
            Array.Copy(arrA, result, arrA.Length - 5);
            Array.Copy(arrB, 3, result, arrA.Length - 5, arrB.Length - 3);
            */
            if (arrA.Length < 8) return null;
            int dateLen = arrA.Length - 3  //-3： 尾部校验和2字节 + 55一个节长度。
                + arrB.Length - 3; //-3:头部BB一个字节，长度2个字节。
            byte[] result = new byte[dateLen];
            Array.Copy(arrA, result, arrA.Length - 3); //A包最后3个不拷贝。
            Array.Copy(arrB, 3, result, arrA.Length - 3, arrB.Length - 3);//B包的前3个不拷贝。

            return result;
        }
        #endregion

    }

 

}

